from datetime import datetime, date, timedelta
from typing import List, Optional, Dict, Any, Tuple
from sqlalchemy.ext.asyncio import AsyncSession

from module_speech.dao.speech_usage_dao import ClientUsageDao
from module_speech.entity.do.speech_client_usage_do import SpeechClientUsageDO
from module_speech.entity.vo.speech_client_vo import (
    ClientUsageModel, ClientUsageCreateModel, ClientUsageQueryModel,
    ClientUsageStatsModel, ClientUsageTrendModel
)
from utils.log_util import logger
from exceptions.exception import ServiceException


class ClientUsageService:
    """
    客户端使用统计服务层
    """
    
    @classmethod
    async def record_usage(cls, db: AsyncSession, usage_data: ClientUsageCreateModel) -> ClientUsageModel:
        """
        记录客户端使用情况
        
        :param db: 数据库会话
        :param usage_data: 使用记录数据
        :return: 创建的使用记录
        """
        try:
            # 创建使用记录对象
            usage_record = SpeechClientUsageDO(
                client_user_id=usage_data.client_user_id,
                usage_date=usage_data.usage_date or date.today(),
                request_count=usage_data.request_count or 1,
                success_count=usage_data.success_count or 0,
                failed_count=usage_data.failed_count or 0,
                audio_duration_minutes=usage_data.audio_duration_minutes or 0.0,
                model_used=usage_data.model_used,
                feature_used=usage_data.feature_used or 'upload',
                api_response_time_avg=usage_data.api_response_time_avg or 0.0,
                data_transfer_mb=usage_data.data_transfer_mb or 0.0,
                create_time=datetime.now(),
                update_time=datetime.now()
            )
            
            # 保存到数据库
            created_record = await ClientUsageDao.add_usage_record_dao(db, usage_record)
            await db.commit()
            
            logger.info(f"记录使用统计成功: 用户ID {usage_data.client_user_id}, 时长 {usage_data.audio_duration_minutes}分钟")
            
            return cls._convert_to_usage_model(created_record)
            
        except Exception as e:
            await db.rollback()
            logger.error(f"记录使用统计失败: {str(e)}")
            raise ServiceException(message=f"记录使用统计失败: {str(e)}")
    
    @classmethod
    async def batch_record_usage(cls, db: AsyncSession, usage_records: List[ClientUsageCreateModel]) -> bool:
        """
        批量记录使用情况
        
        :param db: 数据库会话
        :param usage_records: 使用记录列表
        :return: 是否成功
        """
        try:
            usage_objects = []
            for usage_data in usage_records:
                usage_record = SpeechClientUsageDO(
                    client_user_id=usage_data.client_user_id,
                    usage_date=usage_data.usage_date or date.today(),
                    request_count=usage_data.request_count or 1,
                    success_count=usage_data.success_count or 0,
                    failed_count=usage_data.failed_count or 0,
                    audio_duration_minutes=usage_data.audio_duration_minutes or 0.0,
                    model_used=usage_data.model_used,
                    feature_used=usage_data.feature_used or 'upload',
                    api_response_time_avg=usage_data.api_response_time_avg or 0.0,
                    data_transfer_mb=usage_data.data_transfer_mb or 0.0,
                    create_time=datetime.now(),
                    update_time=datetime.now()
                )
                usage_objects.append(usage_record)
            
            # 批量保存
            await ClientUsageDao.batch_add_usage_records(db, usage_objects)
            await db.commit()
            
            logger.info(f"批量记录使用统计成功: {len(usage_records)}条记录")
            
            return True
            
        except Exception as e:
            await db.rollback()
            logger.error(f"批量记录使用统计失败: {str(e)}")
            raise ServiceException(message=f"批量记录使用统计失败: {str(e)}")
    
    @classmethod
    async def get_usage_records(cls, db: AsyncSession, 
                               query_params: ClientUsageQueryModel) -> Dict[str, Any]:
        """
        获取使用记录列表
        
        :param db: 数据库会话
        :param query_params: 查询参数
        :return: 分页结果
        """
        try:
            # 获取记录列表
            records = await ClientUsageDao.get_usage_records_list(db, query_params)
            
            # 获取总数
            total = await ClientUsageDao.count_usage_records(db, query_params)
            
            # 转换为响应格式
            record_list = []
            for record in records:
                record_model = cls._convert_to_usage_model(record)
                record_list.append(record_model.dict())
            
            return {
                'rows': record_list,
                'total': total,
                'page_num': query_params.page_num,
                'page_size': query_params.page_size
            }
            
        except Exception as e:
            logger.error(f"获取使用记录列表失败: {str(e)}")
            raise ServiceException(message=f"获取使用记录列表失败: {str(e)}")
    
    @classmethod
    async def get_monthly_usage_summary(cls, db: AsyncSession, client_user_id: int, 
                                       year: int, month: int) -> ClientUsageStatsModel:
        """
        获取月度使用汇总
        
        :param db: 数据库会话
        :param client_user_id: 客户端用户ID
        :param year: 年份
        :param month: 月份
        :return: 月度使用统计
        """
        try:
            monthly_stats = await ClientUsageDao.get_monthly_usage(db, client_user_id, year, month)
            
            if not monthly_stats:
                # 如果没有数据，返回空统计
                return ClientUsageStatsModel(
                    client_user_id=client_user_id,
                    period_start=date(year, month, 1),
                    period_end=date(year, month, 1),
                    total_requests=0,
                    successful_requests=0,
                    failed_requests=0,
                    total_audio_minutes=0.0,
                    avg_response_time=0.0,
                    total_data_transfer_mb=0.0,
                    most_used_model="",
                    most_used_feature="",
                    success_rate=0.0
                )
            
            return monthly_stats
            
        except Exception as e:
            logger.error(f"获取月度使用汇总失败: {str(e)}")
            raise ServiceException(message=f"获取月度使用汇总失败: {str(e)}")
    
    @classmethod
    async def get_usage_trend(cls, db: AsyncSession, client_user_id: int,
                             start_date: date, end_date: date, 
                             group_by: str = 'day') -> List[ClientUsageTrendModel]:
        """
        获取使用趋势数据
        
        :param db: 数据库会话
        :param client_user_id: 客户端用户ID
        :param start_date: 开始日期
        :param end_date: 结束日期
        :param group_by: 分组方式 ('day', 'week', 'month')
        :return: 趋势数据列表
        """
        try:
            trend_data = await ClientUsageDao.get_usage_trend(
                db, client_user_id, start_date, end_date, group_by
            )
            
            return trend_data
            
        except Exception as e:
            logger.error(f"获取使用趋势失败: {str(e)}")
            raise ServiceException(message=f"获取使用趋势失败: {str(e)}")
    
    @classmethod
    async def get_top_users_by_usage(cls, db: AsyncSession, year: int, month: int,
                                    limit: int = 10) -> List[Dict[str, Any]]:
        """
        获取使用量前N的用户
        
        :param db: 数据库会话
        :param year: 年份
        :param month: 月份
        :param limit: 返回数量限制
        :return: 用户使用排行榜
        """
        try:
            top_users = await ClientUsageDao.get_top_users_by_usage(db, year, month, limit)
            return top_users
            
        except Exception as e:
            logger.error(f"获取用户使用排行失败: {str(e)}")
            raise ServiceException(message=f"获取用户使用排行失败: {str(e)}")
    
    @classmethod
    async def get_model_usage_stats(cls, db: AsyncSession, start_date: date, 
                                   end_date: date) -> List[Dict[str, Any]]:
        """
        获取模型使用统计
        
        :param db: 数据库会话
        :param start_date: 开始日期
        :param end_date: 结束日期
        :return: 模型使用统计
        """
        try:
            model_stats = await ClientUsageDao.get_model_usage_stats(db, start_date, end_date)
            return model_stats
            
        except Exception as e:
            logger.error(f"获取模型使用统计失败: {str(e)}")
            raise ServiceException(message=f"获取模型使用统计失败: {str(e)}")
    
    @classmethod
    async def get_feature_usage_stats(cls, db: AsyncSession, start_date: date,
                                     end_date: date) -> List[Dict[str, Any]]:
        """
        获取功能使用统计
        
        :param db: 数据库会话
        :param start_date: 开始日期
        :param end_date: 结束日期
        :return: 功能使用统计
        """
        try:
            feature_stats = await ClientUsageDao.get_feature_usage_stats(db, start_date, end_date)
            return feature_stats
            
        except Exception as e:
            logger.error(f"获取功能使用统计失败: {str(e)}")
            raise ServiceException(message=f"获取功能使用统计失败: {str(e)}")
    
    @classmethod
    async def calculate_billing_info(cls, db: AsyncSession, client_user_id: int,
                                    year: int, month: int) -> Dict[str, Any]:
        """
        计算账单信息
        
        :param db: 数据库会话
        :param client_user_id: 客户端用户ID
        :param year: 年份
        :param month: 月份
        :return: 账单信息
        """
        try:
            # 获取月度使用统计
            monthly_usage = await ClientUsageDao.get_monthly_usage(db, client_user_id, year, month)
            
            if not monthly_usage:
                return {
                    'client_user_id': client_user_id,
                    'billing_period': f"{year}-{month:02d}",
                    'total_minutes': 0.0,
                    'total_requests': 0,
                    'billing_amount': 0.0,
                    'currency': 'CNY',
                    'plan_type': 'free'
                }
            
            # 简单的计费逻辑（实际应该根据不同计划类型计算）
            rate_per_minute = 0.1  # 每分钟0.1元
            base_fee = 0.0
            
            billing_amount = monthly_usage.total_audio_minutes * rate_per_minute + base_fee
            
            return {
                'client_user_id': client_user_id,
                'billing_period': f"{year}-{month:02d}",
                'total_minutes': monthly_usage.total_audio_minutes,
                'total_requests': monthly_usage.total_requests,
                'rate_per_minute': rate_per_minute,
                'base_fee': base_fee,
                'billing_amount': round(billing_amount, 2),
                'currency': 'CNY',
                'plan_type': 'paid'
            }
            
        except Exception as e:
            logger.error(f"计算账单信息失败: {str(e)}")
            raise ServiceException(message=f"计算账单信息失败: {str(e)}")
    
    @classmethod
    async def export_usage_report(cls, db: AsyncSession, client_user_id: int,
                                 start_date: date, end_date: date) -> Dict[str, Any]:
        """
        导出使用报告
        
        :param db: 数据库会话
        :param client_user_id: 客户端用户ID
        :param start_date: 开始日期
        :param end_date: 结束日期
        :return: 使用报告数据
        """
        try:
            # 获取期间内的所有记录
            query_params = ClientUsageQueryModel(
                client_user_id=client_user_id,
                start_date=start_date,
                end_date=end_date,
                page_num=1,
                page_size=10000  # 获取所有记录
            )
            
            records = await ClientUsageDao.get_usage_records_list(db, query_params)
            
            # 计算汇总信息
            total_requests = sum(record.request_count for record in records)
            total_success = sum(record.success_count for record in records)
            total_failed = sum(record.failed_count for record in records)
            total_minutes = sum(record.audio_duration_minutes for record in records)
            total_data_mb = sum(record.data_transfer_mb for record in records)
            
            # 计算平均响应时间
            response_times = [record.api_response_time_avg for record in records if record.api_response_time_avg > 0]
            avg_response_time = sum(response_times) / len(response_times) if response_times else 0
            
            # 统计使用的模型和功能
            models_used = {}
            features_used = {}
            
            for record in records:
                if record.model_used:
                    models_used[record.model_used] = models_used.get(record.model_used, 0) + record.request_count
                if record.feature_used:
                    features_used[record.feature_used] = features_used.get(record.feature_used, 0) + record.request_count
            
            # 转换记录为导出格式
            export_records = []
            for record in records:
                export_records.append({
                    'date': record.usage_date.isoformat(),
                    'requests': record.request_count,
                    'success': record.success_count,
                    'failed': record.failed_count,
                    'audio_minutes': record.audio_duration_minutes,
                    'model': record.model_used,
                    'feature': record.feature_used,
                    'response_time': record.api_response_time_avg,
                    'data_mb': record.data_transfer_mb
                })
            
            return {
                'client_user_id': client_user_id,
                'report_period': {
                    'start_date': start_date.isoformat(),
                    'end_date': end_date.isoformat()
                },
                'summary': {
                    'total_requests': total_requests,
                    'successful_requests': total_success,
                    'failed_requests': total_failed,
                    'success_rate': round((total_success / total_requests * 100) if total_requests > 0 else 0, 2),
                    'total_audio_minutes': total_minutes,
                    'total_data_transfer_mb': total_data_mb,
                    'avg_response_time_ms': round(avg_response_time, 2)
                },
                'models_used': models_used,
                'features_used': features_used,
                'daily_records': export_records,
                'generated_at': datetime.now().isoformat()
            }
            
        except Exception as e:
            logger.error(f"导出使用报告失败: {str(e)}")
            raise ServiceException(message=f"导出使用报告失败: {str(e)}")
    
    @classmethod
    async def cleanup_old_records(cls, db: AsyncSession, days_to_keep: int = 365) -> int:
        """
        清理旧的使用记录
        
        :param db: 数据库会话
        :param days_to_keep: 保留天数
        :return: 清理的记录数
        """
        try:
            cutoff_date = date.today() - timedelta(days=days_to_keep)
            deleted_count = await ClientUsageDao.delete_old_records(db, cutoff_date)
            await db.commit()
            
            logger.info(f"清理旧使用记录成功: 删除 {deleted_count} 条记录")
            
            return deleted_count
            
        except Exception as e:
            await db.rollback()
            logger.error(f"清理旧使用记录失败: {str(e)}")
            raise ServiceException(message=f"清理旧使用记录失败: {str(e)}")
    
    @classmethod
    def _convert_to_usage_model(cls, usage_record: SpeechClientUsageDO) -> ClientUsageModel:
        """
        将DO对象转换为VO模型
        
        :param usage_record: 使用记录DO对象
        :return: 使用记录VO模型
        """
        return ClientUsageModel(
            usage_id=usage_record.usage_id,
            client_user_id=usage_record.client_user_id,
            usage_date=usage_record.usage_date,
            request_count=usage_record.request_count,
            success_count=usage_record.success_count,
            failed_count=usage_record.failed_count,
            audio_duration_minutes=usage_record.audio_duration_minutes,
            model_used=usage_record.model_used,
            feature_used=usage_record.feature_used,
            api_response_time_avg=usage_record.api_response_time_avg,
            data_transfer_mb=usage_record.data_transfer_mb,
            create_time=usage_record.create_time,
            update_time=usage_record.update_time
        ) 